/**
* \file main.c
* \brief mbootload的主程序
*   1.mbootload:
*        1.实现boot_drv中的接口函数
*        2.定义相关地址(下载,执行,标志等)
*
*   2.app:
############对于STM32F103和STM32F429
*               1.设置app程序起始地址,存储空间大小(MDK设置即可)
*               2.设置中断向量表偏移量(SystemInit函数内设置)
*               3.设置编译后运行fromelf.exe,生成bin文件(MDK设置即可)
*                        fromelf.exe --bin -o "$L@L.bin" "#L"
############对于NXP RT1052
                                1.中断向量表根据.sct或者.scf定义的。
                                2.中断向量表的具体位置位于RESET中。

############对于stm32f030--2020-05-28
*               1.设置app程序起始地址,ram起始地址。
*               2.重映射sram首地址到0x00000000,同时读取中断向量表到sram 0x20000000地址
*                            3.设置编译后运行fromelf.exe,生成bin文件(MDK设置即可)
*               fromelf.exe --bin -o "$L@L.bin" "#L"
*
* \author mirlee
* \version 0.2
* \date 2019-08-02
*/
#include "boot_drv.h"
#include "ymodem.h"
#include "string.h"
void mbl_upgrade_menu(void);
int mbl_check_upgrade_flag(void);
void mbl_clear_upgrade_flag(void);
int mbl_check_stack_head(unsigned long execAdr);
void mbl_execute_app(volatile unsigned long appAdr);
void mbl_app_dowload(volatile unsigned long appAdr);
int mbl_check_key(void);
void Check_and_Set_Pin(unsigned char Pin_Num); 
extern void System(void);
typedef void (*bootApp)(void);
bootApp bootAppM;

#ifndef NXPRT1052x
extern char USART_Port;
#endif

#ifndef STM32F030x
// static __inline void __set_MSP(unsigned long topOfMainStack)
// {
//     register unsigned long __regMainStackPointer     __asm("msp");
//     __regMainStackPointer = topOfMainStack;
// }
#endif

int main()
{
    __disable_irq();
#if (NXPRT1052x)
    System();
#endif

    /*检查升级标志位*/
    if (mbl_check_upgrade_flag() == 0)
    {
bml_upgrade_menu:
        /*升级菜单*/
        mbl_upgrade_menu();
    }
    else
    {
        /*执行用户程序*/
        mbl_execute_app(MBL_APP_EXEC_ADR);
    }
    while (1)
    {
        mbl_print_str("no program, enter 0 to download\r\n");
        if (mbl_check_key() == '0')
        {
            goto bml_upgrade_menu;
        }
    }
}

//喂独立看门狗,8s左右喂狗
void iwdg_feed(void)
{
#ifndef NXPRT1052x
    IWDG->KR = 0XAAAA; //reload
#endif
}


#ifndef NXPRT1052x
static void My_NVIC_SystemReset(void)
{
#ifndef NXPRT1052x
    SCB->AIRCR  = ( (0x5FA << SCB_AIRCR_VECTKEY_Pos)      |
                    (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
                    SCB_AIRCR_SYSRESETREQ_Msk);
    __DSB();       /* Ensure completion of memory access */
    while (1);        /* wait until reset */
#else
    __NVIC_SystemReset();
#endif
}
#endif

void mbl_upgrade_menu(void)
{
    int key = 0;
    mbl_print_str(  "-------------------------\r\n"
                    "|  Bootloader Menu Show  | \r\n"
                    "-------------------------\r\n");
#ifndef NXPRT1052x
    Check_and_Set_Pin(BLUETOOTH_VCT_PIN);
#endif
    while (1)
    {
        mbl_print_str(  "|dowload bin file enter 1|\r\n"
                        "|execute app      enter 2|\r\n"
                        "-------------------------\r\n");
        key = mbl_check_key();
        if ( key == '1')
        {
            mbl_print_str("\r\nTime to Ymodem\r\n");
        }
#ifndef NXPRT1052x
        // 关闭对应的另外一个串口
        if ( USART_Port == 1  && key == '1')
        {
            mbl_print_str("\r\n Port1 is Ready,Time to Ymodem\r\n");
        }
        else if ( USART_Port == 7  && key == '1' )
        {
            mbl_print_str("\r\n Port7 is Ready,Time to Ymodem\r\n");
        }
#endif
        if (key == '1')
        {
            /*接收bin*/
            mbl_app_dowload(MBL_APP_WR_ADR);
        }
        else if (key == '2')
        {
            /*擦除升级标志位*/
            mbl_clear_upgrade_flag();
            /*执行用户程序*/
            mbl_execute_app(MBL_APP_EXEC_ADR);
            /*复位程序，重新启动*/
            //My_NVIC_SystemReset();
        }
    }
}

void mbl_execute_app(volatile unsigned long appAdr)
{
    unsigned long jumpaddr;
    //程序首个字里存的内容为栈顶地址
    if (mbl_check_stack_head(appAdr) == 0)
    {
        jumpaddr = (*(volatile unsigned long *)(appAdr + 4));
        bootAppM = (bootApp)jumpaddr;
        __set_MSP(*(volatile unsigned long *)appAdr);
        bootAppM();
    }
    else
    {
        mbl_print_str("\r\n no program \r\n");
    }
}
/**
 * @brief mbl_check_key
 * @note 阻塞方式检测输入,
 * @param
 * @retval -1,error
 */
int mbl_check_key(void)
{
    unsigned char key = 0;
    while (1)
    {
        mbl_recv_byte(&key, 100);
        if (key > 0)
            break;
    }
    return key;
}
/**
 * @brief mbl_check_key
 * @note 阻塞方式检测输入,具有超时时间
 * @param
 * @retval -1,error
 */
int mbl_check_key_method2(int Val)
{
    unsigned char key = 0;
    mbl_print_str("Please Entry '1' Confime Download APP else wait 2 Sec.....\r\n ");
    while (Val --)
    {
        mbl_recv_byte(&key, 100);
        if (key > 0)
            break;
    }
    return key;
}

/**
 * @function: mbl_app_dowload
 * @brief:
 * @param {volatile unsigned long} appAdr
 * @return {*}
 * @author: lzc
 */
void mbl_app_dowload(volatile unsigned long appAdr)
{
    long Size = 0;
    mbl_print_str("\r\n Please transmit the bin with Ymodem \r\n");
    mbl_flash_lock_or(0);
    Size = Ymodem_Receive(MBL_APP_WR_ADR);

    if (Size > 0)
    {
        mbl_clear_upgrade_flag();   //擦除IAP升级标志位存放页
        mbl_print_str("\r\n----------------------\r\n ");
        mbl_print_str("\r\n-------cmpleted!------\r\n ");
        mbl_print_str("\r\n----------------------\r\n ");
        mbl_print_str("\r\n----Upgrade File name:\r\n");
        mbl_print_str("\r\n----------------------\r\n ");
        mbl_print_str("\r\n--");
        mbl_print_str((char *)file_name);
        mbl_print_str("--\r\n ");
        mbl_print_str("\r\n----------------------\r\n ");
        mbl_execute_app(MBL_APP_EXEC_ADR);
    }
    else if (Size == MBL_YMODEM_ABORTED_ERROR)
    {
        mbl_print_str("aborted by user.\r\n");
    }
    else if (Size == MBL_FLASH_ERASE_ERROR)
    {
        mbl_print_str("flash erase error\r\n");
    }
    else if (Size == MBL_FLASH_WRITE_ERROR)
    {
        mbl_print_str("flash write error\r\n");
    }
    else
    {
        mbl_print_str("failed to receive\n\r");
    }
}

/**
 * @function: mbl_print_str
 * @brief: dy
 * @param {char} *str
 * @return {*}
 * @author: lzc
 */
void mbl_print_str(char *str)
{
    while (1)
    {
        if (*str != '\0')
        {
            mbl_send_byte(*str);
            str++;
        }
        else
        {
            mbl_send_byte('\0');
            return;
        }
    }
}

/**
 * @function: mbl_check_upgrade_flag
 * @brief: 检查upgrade的标志位
 * @param {*}
 * @return {*}
 * @author: lzc
 */
int mbl_check_upgrade_flag(void)
{
    int key = 0;
    volatile unsigned long  Upgrade_Val = *((volatile unsigned long *)MBL_UP_FLAG_ADR);
#if (STM32F407x)
#if (BL_CHECK_METHOD == 2)
    key = mbl_check_key_method2(100);
    if (key == '1')
        return 0;
    else
        return -1;
#elif (BL_CHECK_METHOD == 1)
    unsigned char Upgrade_Val_H = 0;
    unsigned char Upgrade_Val_L = 0;

    Upgrade_Val = Upgrade_Val & 0xffff;
    Upgrade_Val_H = Upgrade_Val & 0x00ff;
    Upgrade_Val_L = Upgrade_Val >> 8;
    Upgrade_Val = (Upgrade_Val_H << 8) + Upgrade_Val_L ;
#endif
#endif
#if (STM32F103x)||(STM32F429x||(STM32F030x))||(STM32F407x)
#if (BL_CHECK_METHOD == 2)
    key = mbl_check_key_method2(100);
    if (key == '1')
        return 0;
    else
        return -1;
#elif (BL_CHECK_METHOD == 1)
    if (Upgrade_Val == MBL_UP_FLAG_VAL)
        return 0;
    else
        return -1;
#endif
#elif (NXPRT1052x)
// TODO: support 24CXX EEPROM IIC Or Wait Then Upgrade
#if (BL_CHECK_METHOD == 2)
    key = mbl_check_key_method2(100);
    if (key == '1')
        return 0;
    else
        return -1;
#elif (BL_CHECK_METHOD == 1)
    //24CXX EEPROM IIC Read
#endif
#endif
}

void mbl_clear_upgrade_flag(void)
{
#if (STM32F103x)||(STM32F429x)||(STM32F407x)
    mbl_flash_lock_or(0);
    mbl_flash_erase(MBL_UP_FLAG_ADR, 4);
    mbl_flash_lock_or(1);
#elif (NXPRT1052x)
    return;
#endif
}

int mbl_check_stack_head(unsigned long execAdr)
{
#if (STM32F103x)||(STM32F429x)||(STM32F030x)||(STM32F407x)
    if (((*(volatile unsigned long *)execAdr) & 0x2FFC0000) == 0x20000000)
        return 0;
    else
        return -1;
#elif (NXPRT1052x)
    if (((*(volatile unsigned long *)execAdr) & 0x2FF00000) == 0x20000000)
        return 0;
    else
        return -1;
#endif
}
#ifndef NXPRT1052x
//TODO: 获取蓝牙引脚的状态
uint32_t Read_GPIO_IsOutputPinSet(GPIO_TypeDef *GPIOx, uint32_t PinMask)
{
    return (READ_BIT(GPIOx->ODR, PinMask) == (PinMask));
}

// 检测以及设置引脚
void Check_and_Set_Pin(unsigned char Pin_Num)
{
    if (!Read_GPIO_IsOutputPinSet(GPIOxx, BLUETOOTH_VCT_PIN))
    {
        RCCxx->AHB1ENR |= (1 << 0);
        // 设置端口的模式
        GPIOxx->MODER &= ~(0X03 << (2 * Pin_Num));
        GPIOxx->MODER |= (1 << (2 * Pin_Num));

        // 正常输出模式
        GPIOxx->OTYPER &= ~(1 << Pin_Num);
        GPIOxx->OTYPER |= (0 << Pin_Num);

        // 低速模式
        GPIOxx->OSPEEDR &= ~(0X03 << (2 * Pin_Num));
        GPIOxx->OSPEEDR |= (0 << (2 * Pin_Num));

        // 上下拉使能
        GPIOxx->PUPDR &= ~(0X03 << (2 * Pin_Num));
        GPIOxx->PUPDR |= (1 << (2 * Pin_Num));

        // 开启蓝牙电源
        GPIOxx->ODR |= (1 << Pin_Num);
    }
}
#endif
